﻿//----------------------------------------------------------------
// Desc: 
// Edit: ShowLong, http://blog.csdn.net/showlong
//
// Date Created: 2011-07-11
//
// Update Date : 
//
// Copyright (c) Fuel4D (http://www.fuel4d.com), All rights reserved.
//
//----------------------------------------------------------------
#pragma once


#pragma pack(push, 1)


//----------------------------------------------------------------
// 模型动作
//----------------------------------------------------------------
struct FModelAction
{
    FDECLARE_STRUCT(FModelAction)

    ACHAR szName[eCD_MaxName];      // 动作名
    DWORD nStart, nEnd, nHit;       // 起点帧，结束帧，关键帧点
};
typedef FVector< FModelAction > FV_ModelAction;


//----------------------------------------------------------------
// 模型皮肤更换表
//----------------------------------------------------------------
struct FModelSkinTexture
{
    FDECLARE_STRUCT(FModelSkinTexture)

    ACHAR szFile[eCD_MaxFilename];  // 同目录下文件全名，不是路径，且在fmd文件同级目录里
};
typedef FVector< FModelSkinTexture > FV_ModelSkinTexture;
struct FModelSkin
{
    FDECLARE_STRUCT(FModelSkin)

    ACHAR szName[eCD_MaxName];      // 皮肤名
    FV_ModelSkinTexture vTexFile;   // 对应皮肤文件表
};
typedef FVector< FModelSkin > FV_ModelSkin;


//----------------------------------------------------------------
// 下级附挂信息
//----------------------------------------------------------------
//struct FModelAppendInfo
//{
//    FDECLARE_STRUCT(FModelAppendInfo)
//    BYTE nType;                         // 绑定类型： 0骨骼,1附挂点
//    ACHAR szName[eCD_MaxName];  // 附挂名
//    ACHAR szBindName[eCD_MaxName];// 绑定骨骼或者附挂点名
//    ACHAR szFilename[MAX_PATH];         // 绑定的文件路径
//    FIDHASH nBindHash;                  // 该绑定名的HASH（自动生成的）
//};
//typedef FVector< FModelAppendInfo > FV_ModelAppendInfo;


//----------------------------------------------------------------
// 单个模型文件的配置
//----------------------------------------------------------------
class IFModelFile;
class FModelConfig : public IFMemObject
{
    FDECLARE_CLASS(FModelConfig)

public:
    FVECTOR4 vResize;           // 重置整体包围盒，长、宽、高、缩放（如果不需要重置不要修改此包围盒数据）
    FV_ModelAction vAction;     // 动作列表
    FV_ModelSkin vSkin;         // 皮肤列表（不含默认皮肤）
    //FV_ModelAppendInfo vAppInfo;  // 下级附挂信息

    // 添空设置
    FINLINE void clear();
    // 获取指定名字的动作信息
    FINLINE FModelAction* get_action(const ACHAR* name);
    // 删除指定名称的动作信息
    FINLINE void del_action(const ACHAR* name);
    // 获取指定名字的皮肤信息
    FINLINE FModelSkin* get_skin(const ACHAR* name);
    // 删除指定名字的皮肤信息
    FINLINE void del_skin(const ACHAR* name);
    // 加载模型配置文件
    FINLINE bool load(IFModelFile* fmd);
    FINLINE bool load(IFModelFile* fmd, const BYTE* cfg_buf, size_t size);
    // 保存模型配置文件表
    FINLINE bool save(IFModelFile* fmd);
    // 加载动作模板
    FINLINE void load_action(const ACHAR* filepath);
    // 保存动作模板
    FINLINE void save_action(const ACHAR* filepath);
};


FINLINE void FModelConfig::clear()
{
    vResize = FVECTOR4();
    vAction.clear();
    vSkin.clear();
}
FINLINE FModelAction* FModelConfig::get_action(const ACHAR* name)
{
    for (size_t i = 0; i < vAction.size(); ++i)
    {
        if (FStrcmpA(name, vAction[i].szName) == 0)
            return &vAction[i];
    }
    return 0;
}
FINLINE void FModelConfig::del_action(const ACHAR* name)
{
    for (size_t i = 0; i < vAction.size(); ++i)
    {
        if (FStrcmpA(name, vAction[i].szName) == 0)
        {
            vAction.erase(i);
            return;
        }
    }
}
FINLINE FModelSkin* FModelConfig::get_skin(const ACHAR* name)
{
    for (size_t i = 0; i < vSkin.size(); ++i)
    {
        if (FStrcmpA(name, vSkin[i].szName) == 0)
            return &vSkin[i];
    }
    return 0;
}
FINLINE void FModelConfig::del_skin(const ACHAR* name)
{
    for (size_t i = 0; i < vSkin.size(); ++i)
    {
        if (FStrcmpA(name, vSkin[i].szName) == 0)
        {
            vSkin.erase(i);
            return;
        }
    }
}
FINLINE bool FModelConfig::load(IFModelFile* fmd)
{
    FStringW filename = fmd->GetFilepathW();
    filename += _FTW('c');
    FMemFile file(filename.c_str(), _FTW("rb"));
    if (!file.IsLoad())
        return false;

    return load(fmd, file.Get(), file.FileSize());
}
FINLINE bool FModelConfig::load(IFModelFile* fmd, const BYTE* cfg_buf, size_t size)
{
    FMemFile file(cfg_buf, size, false);

    // 加载重置包围盒
    file.Read(&vResize);

    // 加载动作列表
    size_t act_count = 0;
    file.Read(&act_count);
    if (act_count)
    {
        vAction.reserve(act_count);
        vAction.resize(act_count);
        file.Read(&vAction[0], act_count);
    }

    // 加载皮肤列表
    size_t skn_count = 0;
    file.Read(&skn_count);
    if (skn_count)
    {
        vSkin.reserve(skn_count);
        vSkin.resize(skn_count);
        // 加载每个皮肤下面各贴图文件名，不是路径
        for (size_t i = 0; i < vSkin.size(); ++i)
        {
            FModelSkin& skn = vSkin[i];
            file.Read(skn.szName, eCD_MaxName);
            size_t tex_count = 0;
            file.Read(&tex_count);
            if (tex_count != fmd->GetTexCount())
                return false;
            if (tex_count)
            {
                skn.vTexFile.reserve(tex_count);
                skn.vTexFile.resize(tex_count);
                file.Read(&skn.vTexFile[0], tex_count);
            }
        }
    }

    return true;
}
FINLINE bool FModelConfig::save(IFModelFile* fmd)
{
    FStringW filename = fmd->GetFilepathW();
    filename += _FTW('c');

    if (vResize == FVECTOR4() && vAction.empty() && vSkin.empty())
    {
        FDelDirectoryW(filename.c_str(), true);
        return false;
    }

    FMemFile file(2048);

    // 保存重置包围盒
    file.Write(&vResize);

    // 加载动作列表
    size_t act_count = vAction.size();
    file.Write(&act_count);
    if (act_count)
        file.Write(&vAction[0], act_count);

    // 保存皮肤列表
    size_t skn_count = vSkin.size();
    file.Write(&skn_count);
    if (skn_count)
    {
        for (size_t i = 0; i < vSkin.size(); ++i)
        {
            // 保存每个皮肤下面各贴图文件名，不是路径
            FModelSkin& skn = vSkin[i];
            file.Write(skn.szName, eCD_MaxName);
            if (skn.vTexFile.size() != fmd->GetTexCount())
                return false;
            size_t tex_count = skn.vTexFile.size();
            file.Write(&tex_count);
            if (tex_count)
                file.Write(&skn.vTexFile[0], tex_count);
        }
    }

    file.SaveFileW(filename.c_str());

    return true;
}
FINLINE void FModelConfig::load_action(const ACHAR* filepath)
{
    vAction.clear();
    IFINI* ini = FNewINI(FNEW_INFO);
    if (ini->OpenA(filepath))
    {
        if (ini->OpenMuster(_T("fmodel_action")))
        {
            vAction.resize(ini->GetInt(_T("count")));
            for (size_t i = 0; i < vAction.size(); ++i)
            {
                FModelAction& act = vAction[i];
                FStrcpyA(act.szName, ini->GetStringA(FAssertFmtA(_FTA("name_%d"), i)));
                act.nStart = (DWORD)ini->GetIntA(FAssertFmtA(_FTA("start_%d"), i));
                act.nEnd = (DWORD)ini->GetIntA(FAssertFmtA(_FTA("end_%d"), i));
                act.nHit = (DWORD)ini->GetIntA(FAssertFmtA(_FTA("hit_%d"), i));
            }
        }
    }
    FDel(ini);
}
FINLINE void FModelConfig::save_action(const ACHAR* filepath)
{
    IFINIW* ini = FNewINIWA(FNEW_INFO, filepath);
    ini->WriteMuster(_T("fmodel_action"));
    ini->WriteInt(_T("count"), (int)vAction.size());
    for (size_t i = 0; i < vAction.size(); ++i)
    {
        FModelAction& act = vAction[i];
        ini->WriteStringA(FAssertFmtA(_FTA("name_%d"), i), act.szName);
        ini->WriteIntA(FAssertFmtA(_FTA("start_%d"), i), (int)act.nStart);
        ini->WriteIntA(FAssertFmtA(_FTA("end_%d"), i), (int)act.nEnd);
        ini->WriteIntA(FAssertFmtA(_FTA("hit_%d"), i), (int)act.nHit);
    }
    FDel(ini);
}

#pragma pack(pop)